/** @file         ring_buffer_memory_pool.c
 *  @brief        环形缓冲区内存池的实现
 *  @author       Schips
 *  @date         2020-10-27 00:00:50
 *  @version      v1.0
 *  @copyright    Copyright By Schips, All Rights Reserved
 *
 **********************************************************
 *
 *  @attention    NOTE
 *                SDK: 
 *                ENV: 
 *  @par 修改日志:
 *  <table>
 *  <tr><th>Date       <th>Version   <th>Author    <th>Description
 *  <tr><td>2020-10-27 <td>1.0       <td>Schips    <td>创建初始版本
 *  <tr><td>2020-11-14 <td>1.1       <td>Schips    <td>允许注册多个名字的池，以支持不同的业务。
 *  </table>
 *
 **********************************************************
 */

/**************************************** 定义、引用 ****************************************/
#include "ring_buffer_memory_pool.h"
#include "ring_buffer_memory_pool_platform.h"

// 最多支持的业务数量
#ifndef MAX_BM_COUNT
#define MAX_BM_COUNT 3 
#endif
/*! \enum RETURN_STATUS
 *
 *  Detailed 函数返回值
 */
enum RETURN_STATUS {
    // 错误值开始
    NO_ENOUGH_MEM = -10,// 内存不足
    INV_ARG,      // 无效参数
    // 正确值
    OK = 0,
};

// 每一个内存块 的 信息
struct mem_block {
    unsigned int addr_size;         // 数据长度
    //unsigned int skip_size;       // 不得不跳过的、浪费的字节数
    char *            addr;         // 数据域(malloc 返回值)
    struct mem_block* next;         // 下个地址
};

// 环形缓冲区 关心 数据的分配
struct ring_buffer {
    char *addr;                     // 池对象
    unsigned int size;              // 总大小(字节)
    unsigned int used;              // 当前使用(字节)
    char *w_ptr;                    // 写指针
    // 用来拓展内存池 的容量
    struct ring_buffer *prev;
    struct ring_buffer *next;
};

// 池 管理器 对外负责管理内存的申请、释放
struct pool_manager {
    struct mem_block* first;        // 最先申请的块
    struct mem_block* newest;       // 最新申请的块
    char * avalble_addr;            // 可用的首地址 available address
    unsigned int cur_mem_blk_cnt;   // 注册了多少块(malloc时+1，free反之-1)
    struct ring_buffer pool;        // 池信息

    // 用于支持多个块，以支持不同的业务
    char * name;
};

/**************************************** 内部对象 ****************************************/
static struct pool_manager pool_manager_group[MAX_BM_COUNT];


/**************************************** 内部函数区 ****************************************/
// 基本的可用内存检查
int fifo_avalbe_addr_check(struct pool_manager *pool_manager, unsigned int size)
{
    struct ring_buffer* pool = &(pool_manager->pool);

    // 判断 是否超出 可用容量
    if(size + pool->used > pool->size)
    {
        return NO_ENOUGH_MEM;
    }
    return OK;
}

// 计算下一个可用的空间
char* fifo_w_prt_forward(struct pool_manager* bm, unsigned int data_len)
{
    //struct ring_buffer* pool = &(pool_manager->pool);
    struct ring_buffer* pool = &(bm->pool);

    /* 判断是否需要指针归位, 需要解决 地址被截断的情况 */
    unsigned int ptr_distance; // 指针之间相差的大小(超出范围地址 - 容器尾部地址 = 距离)  
    unsigned int empty_but_need_to_skip;// 遇到写指针接近尾部，但需要申请的内存 超过了间隙，而指针读写要求不能截断 
    unsigned int total_cost;// 在上面这种情况下算出来的总消耗 

    if(!pool)  return NULL;

    // 判断 是否超出 可用容量
    if(data_len + pool->used > pool->size)
    {
        return NULL;
    }
    
    //pool->used = 2, pool->w_ptr += 7;// 测试代码

    // 当指针接近容器尾部，因涉及到前进的步长，因此需要计算指针的最终位置
    if( (pool->w_ptr + data_len) >= (pool->addr+ pool->size -1) )
    {
        // 指针之间相差的大小(超出范围地址 - 容器尾部地址 = 距离)
        ptr_distance = (pool->w_ptr + data_len) - (pool->addr+ pool->size);
        // 需要跳过的内存大小（因为不足以放下连续的地址）
        empty_but_need_to_skip = data_len - ptr_distance;
        //PLPRINT("need_to_skip : %d\n", empty_but_need_to_skip);
        // 因此，总的花费 = 跳过的内存大小 + 从头部重新开始计算的大小
        total_cost = data_len + empty_but_need_to_skip; 
        //PLPRINT("total_cost   : %d\n", total_cost);
        // 如果在这种情况下，容器还有剩余空间，那么就可以放下
        if(total_cost + pool->used <= pool->size)
        {
            //PLPRINT("OK, insert\n");
            pool->w_ptr = pool->addr + data_len;
            // 将跳过的内存视为已经使用过的
            pool->used += empty_but_need_to_skip;
        }else {
            return NULL;
        }
    }else {
        //PLPRINT("OK, insert %d\n", data_len);
        pool->w_ptr += data_len;
    }

    return pool->w_ptr;
}

// 重置 块信息
void reset_pool_manager(struct pool_manager *pool_manager)
{
    if(!pool_manager) return ;
    // 相当于重置管理信息
    pool_manager->avalble_addr   = pool_manager->pool.addr;
    pool_manager->newest         = NULL;
    pool_manager->cur_mem_blk_cnt = 0; 
    // 因为有可能存在 empty_but_need_to_skip ，但是既然都获取不到了，那么直接置零即可
    pool_manager->pool.used     = 0; 
}

// 从 块管理器 中 获取 最早申请的内存
char* try_get_first(struct pool_manager *pool_manager, unsigned int *size)
{
    unsigned int addr_size;
    struct mem_block *block;

    if(!pool_manager)       return NULL;
    if(!pool_manager->name) return NULL;

    block = pool_manager->first;
    if(!block) // 已经获取不到内存了 
    {
        reset_pool_manager(pool_manager);
        if(size) *size = 0;

        return NULL;
    }else 
    {
        addr_size  = block->addr_size;
        if(size) *size = addr_size;
    }

    return block->addr;
}

// 从 块管理器 中 释放 最早申请的内存
void try_free_first(struct pool_manager *pool_manager)
{
    unsigned int addr_size;
    struct mem_block *block;
    block = pool_manager->first;

    if(!pool_manager)       return;
    if(!pool_manager->name) return;

    if(!block) // 没有内存可释放
    {
        reset_pool_manager(pool_manager);
    }else
    {
        // 释放对应的内存
        addr_size  = block->addr_size;
        // 指向下一个内存
        pool_manager->first = pool_manager->first->next;

        // 管理信息 更新
        pool_manager->cur_mem_blk_cnt--;
        pool_manager->pool.used -= (addr_size + sizeof(struct mem_block));
    }
}

// 初始化块管理器
void init_pool_manager(struct pool_manager * bm, 
                char* pool_addr, 
                unsigned int pool_size,
                char * name)
{
    if(!bm) return ;

    bm->pool.addr       = pool_addr;
    bm->pool.size       = pool_size; 
    bm->pool.used       = 0; 
    bm->pool.w_ptr      = pool_addr;

    bm->first           = NULL;
    bm->avalble_addr    = bm->pool.addr;
    bm->newest          = NULL;
    bm->cur_mem_blk_cnt = 0; 

    // 如果名字 指向 NULL，代表这片内存没有被人使用
    bm->name            = name;
}

// 字符串比较函数
int __strcmp(const char* src, const char* dst)
{
	int ret = 0 ; 

	while(!(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 
					++src, ++dst; 

	if(ret < 0) 
		ret = -1 ; 
	else if ( ret > 0 ) 
		ret = 1 ; 

	return ret ; 
}

// 根据名字找到 存在的 池
int rbmp_get_pool_id(const char * name)
{
    int i;

    if(!name) return INV_ARG;

    for(i = 0; i < MAX_BM_COUNT; i++)
    {
        if(!pool_manager_group[i].name) break; //continue;
        if(!__strcmp(pool_manager_group[i].name, name))
        {
            return i;
        }
    }

    return INV_ARG;
}

// 找到 对应的 块 管理器
struct pool_manager* get_pool_id_addr(const char * pool_manager_name)
{
    int index;
    if(!pool_manager_name) return NULL;
    index = rbmp_get_pool_id(pool_manager_name);
    if(index < 0) return NULL;

    if(index >= MAX_BM_COUNT) return NULL;

    return &pool_manager_group[index];
}

// 尝试从 块管理器中 申请 内存
char * try_malloc(struct pool_manager *pool_manager, unsigned int size)
{
    int ret;
    struct mem_block *block = NULL;
    unsigned int need_bytes;

    // 如果没有内存可用
    if(!pool_manager->avalble_addr) return NULL;

    // 检查可用地址满足使用
    need_bytes = size + sizeof(struct mem_block);
    ret = fifo_avalbe_addr_check(pool_manager, need_bytes);
    if(ret != OK) return NULL;

    // 设置 内存块
    block = (struct mem_block *)pool_manager->avalble_addr;
    block->addr = (char*)(block + 1); // 将 addr 指针指向 紧跟着 头 以后的 地址
    block->addr_size = size; 
    block->next = NULL;

    // 准备下一个可用地址
    pool_manager->avalble_addr = fifo_w_prt_forward(pool_manager, need_bytes);

    // 将块的信息记录进管理器中，作为单链表中的节点
    if(!pool_manager->first) // 记录第一个块
    {
        pool_manager->first = block;
    }

    // 根据 newest 可以实现尾插法 
    if(!pool_manager->newest)
    {
        pool_manager->newest = block;
    }else 
    {
        // 尾插，需要保存一些关键信息。
        pool_manager->newest->next = block;
        // 更新 newest 为 最新的结点
        pool_manager->newest = block;
    }

    // 更新管理信息
    pool_manager->cur_mem_blk_cnt++;
    pool_manager->pool.used += need_bytes;

    return block->addr;
}

/**************************************** 外部函数区 ****************************************/
// 申请内存 (默认 使用第一块 内存块, 不允许多个业务使用同一块)
char* rbmp_malloc(unsigned int size)
{
    return rbmp_malloc_by_id(0, size);
}

// 申请内存(根据名字来寻找)
char* rbmp_malloc_by_name(const char * name, unsigned int size)
{
    struct pool_manager *pool_manager = NULL;

    pool_manager = get_pool_id_addr(name);
    if(!pool_manager) return NULL;

    return try_malloc(pool_manager, size);
}

// 申请内存(根据ID来寻找)
char* rbmp_malloc_by_id(int id, unsigned int size)
{
    struct pool_manager *pool_manager;

    if(id < 0 || id > MAX_BM_COUNT)
        return NULL;

    pool_manager = &pool_manager_group[id];

    return try_malloc(pool_manager, size);
}

// 释放最早申请的内存 (默认使用第一块内存块)
void rbmp_free_first(void)
{
    rbmp_free_first_by_id(0);
}

// 释放最早申请的内存(根据名字寻找)
void rbmp_free_first_by_name(const char* name)
{
    struct pool_manager *pool_manager = NULL;

    pool_manager = get_pool_id_addr(name);
    if(!pool_manager) return;

    try_free_first(pool_manager);
}

// 释放最早申请的内存(根据ID寻找)
void rbmp_free_first_by_id(int id)
{
    struct pool_manager *pool_manager = NULL;

    if(id < 0 || id > MAX_BM_COUNT)
        return;

    pool_manager = &pool_manager_group[id];

    if(!pool_manager->name) return;

    try_free_first(pool_manager);
}

// 获取最早申请的内存(默认使用第一块内存块)
char* rbmp_get_first(unsigned int *size)
{
    return rbmp_get_first_by_id(0, size);
}

// 获取最早申请的内存(根据名字寻找)
char* rbmp_get_first_by_name(const char* name, unsigned int *size)
{
    struct pool_manager *pool_manager;

    pool_manager = get_pool_id_addr(name);
    if(!pool_manager) return NULL;

    return try_get_first(pool_manager, size);
}

// 获取最早申请的内存(根据ID寻找)
char* rbmp_get_first_by_id(int id, unsigned int *size)
{
    struct pool_manager *pool_manager;

    if(id < 0 || id > MAX_BM_COUNT)
        return NULL;

    pool_manager = &pool_manager_group[id];

    return try_get_first(pool_manager, size);
}

// 注册一个池
int rbmp_add_pool(char *name, char* pool_addr, unsigned int pool_size)
{
    struct pool_manager * bm;
    int i, index;

    if(!pool_addr)  return INV_ARG;
    if(!name)       return INV_ARG;
    if(!pool_size)  return INV_ARG;

    // 策略： 找块，找不到时 尾插
    index = rbmp_get_pool_id(name);
    // 找到了，但是存在错误
    if(index >= MAX_BM_COUNT)
        return INV_ARG;

    if(index < 0)
    {
        // 意味着没找到，插入到 最新的位置
        for (i = 0, index = 0; i < MAX_BM_COUNT; ++i)
        {
            bm = &pool_manager_group[i];
            if(!bm->name)
            {
                index = i;
                break;
            }
        }
    }

    bm = &pool_manager_group[index];

    // 初始化 内存数据
    for(i = 0; i < pool_size; i++)
    {
        pool_addr[i] = 0;
    }

    init_pool_manager(bm, pool_addr, pool_size, name);

    return OK;
}

// 删除一个注册的池(数组前移)
int rbmp_remove_pool(const char *name)
{
    int i;
    int index;
    struct pool_manager* bm_next, *bm_now;
    if(!name) return INV_ARG;

    index = rbmp_get_pool_id(name);

    // 找到了，但是存在错误  或 失败
    if((index >= MAX_BM_COUNT) || (index < 0))
        return INV_ARG;

    for (i = index; i < MAX_BM_COUNT - 1; ++i) 
    {

        bm_now  = &pool_manager_group[i]; 
        bm_next = &pool_manager_group[i+1]; 

        // 遇到空 时 提前结束
        if(!bm_now->name) break;

#if 1
        bm_now->pool     = bm_next->pool;
#else
        bm_now->pool.addr     = bm_next->pool.addr;
        bm_now->pool.size     = bm_next->pool.size; 
        bm_now->pool.used     = bm_next->pool.used; 
        bm_now->pool.w_ptr    = bm_next->pool.w_ptr;
#endif

        bm_now->first         = bm_next->first;
        bm_now->avalble_addr  = bm_next->avalble_addr;
        bm_now->newest        = bm_next->newest;

        bm_now->cur_mem_blk_cnt = bm_next->cur_mem_blk_cnt; 

        bm_now->name          = bm_next->name;
    }

    init_pool_manager(&pool_manager_group[i] , NULL, 0, NULL);

    return OK;
}

// 初始化块，用于后续 添加
unsigned int  rbmp_init(void)
{
    unsigned int i;
    struct pool_manager* bm;

    // 初始化所有的块
    for (i = 0; i < MAX_BM_COUNT; ++i) 
    {
        bm = &pool_manager_group[i];
        // 重置信息
        init_pool_manager(bm , NULL, 0, NULL);
    }

    // 返回 初始化成功的数量
    return i;
}

void rbmp_remove_all_pools(void)
{
    rbmp_init();
}

// 打印所有的信息
void rbmp_dump(void)
{
    struct mem_block* first;
    struct pool_manager* bm;
    int index, i;
#ifdef DUMP_VAR // 打印值
    int j;
#endif

    for (index = 0; index < MAX_BM_COUNT; ++index) {
        bm = &pool_manager_group[index];

        PLPRINT("========%d========\n", index);
        // 块 管理器 关心 执行内存的整体信息
        PLPRINT("[Pool Manager info]\n");
        if(!bm->name) {
            PLPRINT("Pool Manager No.%d is NOT USED\n\n", index);
            continue;
        }
        PLPRINT("first block  : %p\n",  bm->first);
        PLPRINT("curblock cnt : %d\n",  bm->cur_mem_blk_cnt);
        PLPRINT("avalble_addr : %p\n",  bm->avalble_addr);
        PLPRINT("=================\n");
        // 内部的池 关心 数据是如何分配的
        PLPRINT("[Pool info]\n");
        PLPRINT("addr : %p\n", bm->pool.addr);
        PLPRINT("size : %d\n", bm->pool.size);
        PLPRINT("used : %d\n", bm->pool.used);
        first = bm->first;
        for (i = 0; i < bm->cur_mem_blk_cnt; ++i) {
            PLPRINT("=================\n");
            // 内部块 只有最简单 信息
            PLPRINT("[Block] NO.%d at %p\n", i+1, first);
            PLPRINT("len  :%d\n",first->addr_size);
            PLPRINT("addr :%p\n",first->addr);
            PLPRINT("next :%p\n",first->next);
#ifdef DUMP_VAR // 打印值
            for (j = 0; j < first->addr_size; ++j) {
                if(j % 20 == 0) PLPRINT("\n");
                PLPRINT("%2x ", first->addr[j]);
            }
            PLPRINT("\n");
#endif
            first = first->next;
        }
        PLPRINT("\n");
    }
}

